package com.lakeworks.service.impl;

import com.lakeworks.common.core.domain.AjaxResult;
import com.lakeworks.common.enums.OprProductType;
import com.lakeworks.common.enums.PickupType;
import com.lakeworks.common.enums.ShipMentType;
import com.lakeworks.common.exception.response.ResponseException;
import com.lakeworks.common.utils.StringUtils;
import com.lakeworks.domain.OprPickup;
import com.lakeworks.domain.OprProduct;
import com.lakeworks.domain.OprShipment;
import com.lakeworks.domain.status.*;
import com.lakeworks.domain.vo.*;
import com.lakeworks.mapper.*;
import com.lakeworks.service.IOprIndexService;
import com.lakeworks.service.api.MerchantAsync;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static java.util.Optional.ofNullable;

@Service
public class OprIndexServiceImpl implements IOprIndexService {


    @Resource
    private OprPickupMapper oprPickupMapper;

    @Resource
    private OprProductMapper oprProductMapper;

    @Resource
    private OprShipmentMapper oprShipmentMapper;

    @Resource
    private PostVerifyMapper postVerifyMapper;

    @Resource
    private PostQcMapper postQcMapper;

    @Resource
    private PostWrapMapper postWrapMapper;

    @Resource
    private PostSortMapper postSortMapper;

    @Resource
    private PostPackMapper postPackMapper;

    @Resource
    private StaPickupMapper staPickupMapper;
    @Resource
    private StaShipmentMapper staShipmentMapper;


    private final BiFunction<Object,Object,Integer> getSum = (a,b)-> ofNullable(a).map(o -> Integer.parseInt(o.toString())).orElse(0)+ofNullable(b).map(o -> Integer.parseInt(o.toString())).orElse(0);


    @Override
    public MonitorVo getMonitorVo() {
        List<OprPickup> pendings = oprPickupMapper.selectOprPickupList(OprPickup.builder().status(PickupType.PENDING.getValue()).build());
        List<OprPickup> signs = oprPickupMapper.selectPickUpSignAndVerify();
        List<Integer> atFactoryList = oprPickupMapper.selectAtFactory();
//        List<OprPickup> receiveds = oprPickupMapper.selectOprPickupList(OprPickup.builder().status(PickupType.RECEIVED.getValue()).build());
        List<OprShipment> pickeds = oprShipmentMapper.selectOprShipmentList(OprShipment.builder().status(ShipMentType.PICKED.getValue()).build());
        List<OprShipment> shippeds = oprShipmentMapper.selectOprShipmentList(OprShipment.builder().status(ShipMentType.PACKED.getValue()).build());
        return MonitorVo.builder()
                .pendingNum(pendings.size())
                .pendingCount(pendings.stream().mapToInt(OprPickup::getQuantity).sum())
                .signNum(signs.size())
                .signCount(signs.stream().mapToInt(OprPickup::getQuantity).sum())
//                .receivedNum(receiveds.size())
                .receivedNum(atFactoryList.get(0))
//                .receivedCount(getCount(receiveds.stream().map(OprPickup::getId).collect(Collectors.toList()), null, null))
                .receivedCount(atFactoryList.get(1))
                .pickedNum(pickeds.size())
                .pickedCount(getCount(pickeds.stream().map(OprShipment::getId).collect(Collectors.toList()), "1", null))
                .shippedNum(shippeds.size())
                .shippedCount(getCount(shippeds.stream().map(OprShipment::getId).collect(Collectors.toList()), "1", null))
                .build();
    }

    @Override
    public CapacityVo getCapacityVo() {
        List<PostVerify> verifies = postVerifyMapper.selectPostVerifyNow();
        List<Long> receiveds = getLongList(verifies.stream().map(PostVerify::getProductId).collect(Collectors.toList()), null);
        List<PostQc> qcs = postQcMapper.selectPostQcNow();
        List<Long> qualifieds = getLongList(qcs.stream().map(PostQc::getProductId).collect(Collectors.toList()), null);
        List<PostWrap> wraps = postWrapMapper.selectPostWrapNow();
        List<Long> packages = getLongList(wraps.stream().map(PostWrap::getProductId).collect(Collectors.toList()), null);
        List<PostSort> sorts = postSortMapper.selectPostSortNow();
        List<Long> pickings = getLongList(sorts.stream().map(PostSort::getProductId).collect(Collectors.toList()), "1");
        List<PostPack> packs = postPackMapper.selectPostPackNow();
        List<Long> shippeds = getLongList(packs.stream().map(PostPack::getProductId).collect(Collectors.toList()), "1");
        return CapacityVo.builder()
                .receivedNum(receiveds.size())
                .receivedCount(verifies.size())
                .qualifiedNum(qualifieds.size())
                .qualifiedCount(qcs.size())
                .packageNum(packages.size())
                .packageConut(wraps.size())
                .pickingNum(pickings.size())
                .pickingCount(sorts.size())
                .shippedNum(shippeds.size())
                .shippedCount(packs.size())
                .build();
    }

    @Override
    public AgeingVo getAgeingVo() {
        List<Long> thereList = oprProductMapper.selectTimeOutProduct(3);
        List<Long> fivelist = oprProductMapper.selectTimeOutProduct(5);
        List<Long> sevenlist = oprProductMapper.selectTimeOutProduct(7);
        return AgeingVo.builder()
                .ten(oprPickupMapper.selectTimeOutPickUp(10))
                .eighteen(oprPickupMapper.selectTimeOutPickUp(18))
                .twentyFour(oprPickupMapper.selectTimeOutPickUp(24))
                .threeDaysNum(new HashSet<>(thereList).size())
                .threeDays(thereList.size())
                .fiveDaysNum(new HashSet<>(fivelist).size())
                .fiveDays(fivelist.size())
                .sevenDaysNum(new HashSet<>(sevenlist).size())
                .sevenDays(sevenlist.size())
                .build();
    }

    @Override
    public Integer getVerifyCount(Integer hour) {
        return oprPickupMapper.selectVerifyCount(hour);
    }

    @Override
    public Integer getProductCount(String status, Integer hour) {
        String state = ofNullable(status).map(s -> OprProductType.valueOf(status).getValue()).orElseThrow(()->new ResponseException("状态不能为空"));
        if(state.equals(OprProductType.TOWRAP.getValue())){
            return oprProductMapper.selectNoAttchProductCount(hour);
        }else if(state.equals(OprProductType.WRAPPED.getValue())){
            return oprProductMapper.selectAttchProductCount(hour);
        }
        return oprProductMapper.selectProductCount(state,hour);
    }

    @Override
    public Integer getshipmentCount(String status, Integer hour) {
        String state = ofNullable(status).map(s -> ShipMentType.valueOf(status).getValue()).orElseThrow(()->new ResponseException("状态不能为空"));
        if(state.equals(ShipMentType.PENDING.getValue())){
            return oprShipmentMapper.selectPlaceShipmentCount(hour);
        }
        return oprShipmentMapper.selectShipmentCount(state,hour);
    }


    @Override
    public Map<String, Integer> getNotReceivedCount(Integer startHour,Integer endHour) {
        return oprPickupMapper.selectNotReceivedCount(startHour,endHour);
    }

    @Override
    public Map<String,Integer> getCustomerTimeoutCount(Integer startDays,Integer endDays) {
        Map<String, Integer> map = new HashMap<>();
        Map<String, Object> pickupMap = oprPickupMapper.selectPickupCustomerCount(startDays,endDays);
        Map<String, Object> shipmentMap = oprShipmentMapper.selectShipmentCustomerCount(startDays,endDays);
        map.put("orderNum",getSum.apply(pickupMap.get("order_num"),shipmentMap.get("order_num")));
        map.put("productNum",getSum.apply(pickupMap.get("product_num"),shipmentMap.get("product_num")));
        return map;
    }







    @Override
    public Map<String,Integer> getProductionTimeoutCount(Integer startDays,Integer endDays) {
        return oprShipmentMapper.selectShipmentProductionCount(startDays,endDays);
    }

    @Override
    public List<TendencyInfo> getTendency() {
        List<String> dates = IntStream.rangeClosed(1, 10).mapToObj(LocalDate.now()::minusDays).map(l -> l.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).toList();
        return dates.stream().map(d-> TendencyInfo.builder()
                .date(d)
                .pickupNum(staPickupMapper.selectStaPickupByDate(d))
                .shipmentNum(staShipmentMapper.selectStaShipmentByDate(d)).build()).toList();
    }


    private List<Long> getLongList(List<Long> list, String code) {
        Set<Long> set = new HashSet<>();
        list.forEach(l -> {
            OprProduct product = oprProductMapper.selectOprProductById(l);
            if(product != null){
                if (StringUtils.isBlank(code)) {
                    set.add(product.getPickupId());
                } else {
                    set.add(product.getShipmentId());
                }
            }
        });
        return set.stream().toList();
    }

    private Integer getDaysNum(List<Long> list) {
        Set<Long> set = new HashSet<>(list);
        return set.size();
    }



    private Integer getCount(List<Long> list, String code, String status) {
        Integer intStatus = null;
        if (StringUtils.isNotBlank(status)) {
            intStatus = Integer.valueOf(status);
        }
        AtomicReference<Integer> count = new AtomicReference<>(0);
        Integer finalIntStatus = intStatus;
        list.forEach(l -> {
            List<OprProduct> products;
            if (StringUtils.isBlank(code)) {
                products = oprProductMapper.selectIndexProductList(OprProduct.builder().pickupId(l).intStatus(finalIntStatus).build());
            } else {
                products = oprProductMapper.selectIndexProductList(OprProduct.builder().shipmentId(l).intStatus(finalIntStatus).build());
            }
            count.updateAndGet(v -> v + products.size());
        });
        return count.get();
    }


}
